﻿using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Text;

//c:\Program Files\Microsoft SDKs\Windows\v6.0A\Include\WinUser.h

/// <summary>
/// 
/// </summary>
namespace PickGold.Api
{
	/// <summary>
	/// 文件摘要。
	/// </summary>
	public static class FileSummary
	{
		/// <summary/>
		public const string IID_PropertySetStorage = "0000013A-0000-0000-C000-000000000046";
		/// <summary/>
		public const string IID_PropertyStorage = "00000138-0000-0000-C000-000000000046";
		/// <summary/>
		public const string IID_EnumSTATPROPSTG = "00000139-0000-0000-C000-000000000046";
		/// <summary/>
		public const string RFMTID = "F29F85E0-4FF9-1068-AB91-08002B27B3D9";
		/// <summary/>
		public static readonly IPropertySetStorage IPropertySetStorageNull = null;
		/// <summary/>
		public static readonly IPropertyStorage IPropertyStorageNull = null;
		/// <summary/>
		public static readonly IEnumSTATPROPSTG IEnumSTATPROPSTGNull = null;

		#region SummaryPropId

		/// <summary>
		/// 文件摘要属性名
		/// </summary>
		[ComVisible(true)]
		public enum SummaryPropId : int
		{
			/// <summary>
			/// PID_TITLE VT_LPSTR
			/// 标题。
			/// </summary>
			Title = 0x00000002,
			/// <summary>
			/// PID_SUBJECT VT_LPSTR
			/// 主题。
			/// </summary>
			Subject = 0x00000003,
			/// <summary>
			/// PID_AUTHOR VT_LPSTR
			/// 作者。
			/// </summary>
			Author = 0x00000004,
			/// <summary>
			/// PID_KEYWORDS VT_LPSTR
			/// 关键字。
			/// </summary>
			Keywords = 0x00000005,
			/// <summary>
			/// PID_COMMENTS VT_LPSTR
			/// 备注。
			/// </summary>
			Comments = 0x00000006,
			/// <summary>
			/// PID_TEMPLATE VT_LPSTR
			/// 模板。
			/// </summary>
			Template = 0x00000007,
			/// <summary>
			/// PID_LASTAUTHOR VT_LPSTR
			/// 最后保存用户。
			/// </summary>
			LastSavedBy = 0x00000008,
			/// <summary>
			/// PID_REVNUMBER VT_LPSTR
			/// 修订版号码。
			/// </summary>
			RevisionNumber = 0x00000009,
			/// <summary>
			/// PID_EDITTIME VT_FILETIME (UTC)
			/// 最后编辑时间。
			/// </summary>
			EditingTime = 0x0000000A,
			/// <summary>
			/// PID_LASTPRINTED VT_FILETIME (UTC)
			/// 最后打印时间。
			/// </summary>
			LastPrinted = 0x0000000B,
			/// <summary>
			/// PID_CREATE_DTM VT_FILETIME (UTC)
			/// 创建时间。
			/// </summary>
			CreateDateTime = 0x0000000C,
			/// <summary>
			/// PID_LASTSAVE_DTM VT_FILETIME (UTC)
			/// 最后保存时间。
			/// </summary>
			LastSaveDateTime = 0x0000000D,
			/// <summary>
			/// PID_PAGECOUNT VT_I4
			/// 总页数。
			/// </summary>
			PageCount = 0x0000000E,
			/// <summary>
			/// PID_WORDCOUNT VT_I4
			/// 总单词数。
			/// </summary>
			WordCount = 0x0000000F,
			/// <summary>
			/// PID_CHARCOUNT VT_I4
			/// 总字符数。
			/// </summary>
			CharCount = 0x00000010,
			/// <summary>
			/// PID_THUMBNAIL VT_CF
			/// 概览。
			/// </summary>
			Thumbnail = 0x00000011,
			/// <summary>
			/// PID_APPNAME VT_LPSTR
			/// 创建文件的软件名称。
			/// </summary>
			AppName = 0x00000012,
			/// <summary>
			/// PID_SECURITY VT_I4
			/// 安全。
			/// </summary>
			Security = 0x00000013
		}

		#endregion

		#region STGC

		/// <summary>
		/// STGC
		/// </summary>
		[Flags]
		[ComVisible(true)]
		public enum STGC : int
		{
			DEFAULT = 0,
			OVERWRITE = 1,
			ONLYIFCURRENT = 2,
			DANGEROUSLYCOMMITMERELYTODISKCACHE = 4,
			CONSOLIDATE = 8
		}

		#endregion

		#region PROPSETFLAG

		/// <summary>
		/// PROPSETFLAG
		/// </summary>
		[Flags]
		[ComVisible(true)]
		public enum PROPSETFLAG : int
		{
			DEFAULT = 0,
			NONSIMPLE = 1,
			ANSI = 2,
			UNBUFFERED = 4,
			CASE_SENSITIVE = 8
		}

		#endregion

		#region STGM

		/// <summary>
		/// STGM
		/// </summary>
		[Flags]
		[ComVisible(true)]
		public enum STGM : int
		{
			READ = 0x00000000,
			WRITE = 0x00000001,
			READWRITE = 0x00000002,
			SHARE_DENY_NONE = 0x00000040,
			SHARE_DENY_READ = 0x00000030,
			SHARE_DENY_WRITE = 0x00000020,
			SHARE_EXCLUSIVE = 0x00000010,
			PRIORITY = 0x00040000,
			CREATE = 0x00001000,
			CONVERT = 0x00020000,
			FAILIFTHERE = 0x00000000,
			DIRECT = 0x00000000,
			TRANSACTED = 0x00010000,
			NOSCRATCH = 0x00100000,
			NOSNAPSHOT = 0x00200000,
			SIMPLE = 0x08000000,
			DIRECT_SWMR = 0x00400000,
			DELETEONRELEASE = 0x04000000
		}

		#endregion

		#region STGFMT

		/// <summary>
		/// STGFMT
		/// </summary>
		[ComVisible(true)]
		public enum STGFMT : int
		{
			STORAGE = 0,
			FILE = 3,
			ANY = 4,
			DOCFILE = 5
		}

		#endregion

		#region HResults

		/// <summary>
		/// HResults
		/// </summary>
		[ComVisible(true)]
		public enum HResults : uint
		{
			S_OK = 0,
			STG_E_FILEALREADYEXISTS = 0x80030050
		}

		#endregion

		#region STGOptions

		/// <summary>
		/// STGOptions
		/// </summary>
		[StructLayout(LayoutKind.Explicit, Size = 12, CharSet = CharSet.Unicode)]
		public struct STGOptions
		{
			[FieldOffset(0)]
			ushort usVersion;
			[FieldOffset(2)]
			ushort reserved;
			[FieldOffset(4)]
			uint uiSectorSize;
			[FieldOffset(8), MarshalAs(UnmanagedType.LPWStr)]
			string pwcsTemplateFile;
		}

		#endregion

		/// <summary>
		/// 设置文件摘要属性。
		/// </summary>
		/// <param name="file">文件绝对路径。</param>
		/// <param name="values">文件摘要属性值。</param>
		[ComVisible(true)]
		public static int SetSummary(string file, IDictionary<SummaryPropId, object> values)
		{
			if (values == null || values.Count == 0)
				return 1;

			var ps = FileSummary.IPropertyStorageNull;
			var pss = FileSummary.IPropertySetStorageNull;
			try
			{
				var riid = new Guid(FileSummary.IID_PropertySetStorage);
				var result = Ole32.StgOpenStorageEx(file, (int)(STGM.SHARE_EXCLUSIVE | STGM.READWRITE), (int)STGFMT.FILE, 0, IntPtr.Zero, IntPtr.Zero, ref riid, ref pss);
				if (pss == null)
				{
					var i = Kernel32.GetLastError();
					if (i > 0)
						return i;

					return -1;
				}

				var rfmtid = new Guid(FileSummary.RFMTID);
				result = pss.Create(ref rfmtid, IntPtr.Zero, (int)PROPSETFLAG.DEFAULT, (int)(STGM.CREATE | STGM.READWRITE | STGM.SHARE_EXCLUSIVE), ref ps);
				if (result != (uint)HResults.S_OK)
				{
					var i = Kernel32.GetLastError();
					if (i > 0)
						return i;

					return -2;
				}

				foreach (var key in values.Keys)
				{
					var propSpec = new PropSpec();
					propSpec.ulKind = 1;
					propSpec.Name_Or_ID = new IntPtr((int)key);
					var propVariant = new PropVariant();
					propVariant.FromObject(values[key]);
					result = ps.WriteMultiple(1, ref propSpec, ref propVariant, 2);
					if (result != (uint)HResults.S_OK)
					{
						var i = Kernel32.GetLastError();
						if (i > 0)
							return i;

						return -3;
					}
				}
				result = ps.Commit((int)STGC.DEFAULT);
				if (result != (uint)HResults.S_OK)
				{
					var i = Kernel32.GetLastError();
					if (i > 0)
						return i;

					return -4;
				}
			}
			finally
			{
				if (pss != null)
					Marshal.ReleaseComObject(pss);
				if (ps != null)
					Marshal.ReleaseComObject(ps);
				pss = null;
				ps = null;
			}

			return 0;
		}

		/// <summary>
		/// 设置文件摘要属性。
		/// </summary>
		/// <param name="file">文件绝对路径。</param>
		/// <param name="values">文件摘要属性值。</param>
		[ComVisible(true)]
		public static void SetSummary(string file, IDictionary<string, object> values)
		{
			if (values == null || values.Count == 0)
				return;

			var ps = FileSummary.IPropertyStorageNull;
			var pss = FileSummary.IPropertySetStorageNull;
			try
			{
				var riid = new Guid(FileSummary.IID_PropertySetStorage);
				var result = Ole32.StgOpenStorageEx(file, (int)(STGM.SHARE_EXCLUSIVE | STGM.READWRITE), (int)STGFMT.FILE, 0, IntPtr.Zero, IntPtr.Zero, ref riid, ref pss);
				var rfmtid = new Guid(FileSummary.RFMTID);
				result = pss.Create(ref rfmtid, IntPtr.Zero, (int)PROPSETFLAG.DEFAULT, (int)(STGM.CREATE | STGM.READWRITE | STGM.SHARE_EXCLUSIVE), ref ps);
				if (result != (uint)HResults.S_OK)
					return;

				foreach (var key in values.Keys)
				{
					var propSpec = new PropSpec();
					propSpec.ulKind = 0;
					propSpec.Name_Or_ID = Marshal.StringToCoTaskMemUni(key);
					//propSpec.Name_Or_ID = Marshal.StringToHGlobalUni(key);
					var propVariant = new PropVariant();
					propVariant.FromObject(values[key]);
					result = ps.WriteMultiple(1, ref propSpec, ref propVariant, 2);
					Marshal.FreeCoTaskMem(propSpec.Name_Or_ID);
					if (result != (uint)HResults.S_OK)
						return;
				}
				result = ps.Commit((int)STGC.DEFAULT);
				if (result != (uint)HResults.S_OK)
					return;
			}
			finally
			{
				if (pss != null)
					Marshal.ReleaseComObject(pss);
				if (ps != null)
					Marshal.ReleaseComObject(ps);
				pss = null;
				ps = null;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="file"></param>
		/// <param name="keys"></param>
		/// <returns></returns>
		[ComVisible(true)]
		public static object[] GetSummary(string file, params SummaryPropId[] keys)
		{
			if (keys == null || keys.Length == 0)
				return null;

			var os = new object[keys.Length];
			var ps = FileSummary.IPropertyStorageNull;
			var pss = FileSummary.IPropertySetStorageNull;
			try
			{
				var riid = new Guid(FileSummary.IID_PropertySetStorage);
				var result = Ole32.StgOpenStorageEx(file, (int)(STGM.SHARE_EXCLUSIVE | STGM.READ), (int)STGFMT.FILE, 0, IntPtr.Zero, IntPtr.Zero, ref riid, ref pss);
				if (result != (uint)HResults.S_OK)
					return os;

				var rfmtid = new Guid(FileSummary.RFMTID);
				result = pss.Open(ref rfmtid, (int)(STGM.READ | STGM.SHARE_EXCLUSIVE), out ps);
				if (result != (uint)HResults.S_OK)
					return os;

				var propSpecs = new PropSpec[keys.Length];
				var propVariants = new PropVariant[keys.Length];
				for (var i = 0; i < keys.Length; i++)
				{
					propSpecs[i] = new PropSpec();
					propSpecs[i].ulKind = 1;
					propSpecs[i].Name_Or_ID = new IntPtr((int)keys[i]);
					propVariants[i] = new PropVariant();
				}
				//result = ps.ReadMultiple((uint)keys.Length, propSpecs, propVariants);
				//for (var i = 0; i < keys.Length; i++)
				//    os[i] = Marshal.PtrToStringUni(propVariants[i].pointerValue);
				for (var i = 0; i < keys.Length; i++)
				{
					result = ps.ReadMultiple(1, ref propSpecs[i], ref propVariants[i]);
					if (result == (uint)HResults.S_OK)
						os[i] = propVariants[i].ToObject();
				}
				result = ps.Commit((int)STGC.DEFAULT);
				if (result != (uint)HResults.S_OK)
					return os;
			}
			finally
			{
				if (pss != null)
					Marshal.ReleaseComObject(pss);
				if (ps != null)
					Marshal.ReleaseComObject(ps);
				pss = null;
				ps = null;
			}
			return os;
		}

		[ComVisible(true)]
		public static object[] GetSummary(string file, params string[] keys)
		{
			if (keys == null || keys.Length == 0)
				return null;

			var os = new object[keys.Length];
			var ps = FileSummary.IPropertyStorageNull;
			var pss = FileSummary.IPropertySetStorageNull;
			//var esp = FileSummary.IEnumSTATPROPSTGNull; ps.Enum(ref esp);
			try
			{
				var riid = new Guid(FileSummary.IID_PropertySetStorage);
				var result = Ole32.StgOpenStorageEx(file, (int)(STGM.SHARE_EXCLUSIVE | STGM.READ), (int)STGFMT.FILE, 0, IntPtr.Zero, IntPtr.Zero, ref riid, ref pss);
				if (result != (uint)HResults.S_OK)
					return os;

				var rfmtid = new Guid(FileSummary.RFMTID);
				result = pss.Open(ref rfmtid, (int)(STGM.READ | STGM.SHARE_EXCLUSIVE), out ps);
				if (result != (uint)HResults.S_OK)
					return os;

				var propSpecs = new PropSpec[keys.Length];
				var propVariants = new PropVariant[keys.Length];
				for (var i = 0; i < keys.Length; i++)
				{
					propSpecs[i] = new PropSpec();
					propSpecs[i].ulKind = 0;
					//propSpecs[i].Name_Or_ID = Marshal.StringToHGlobalAuto(keys[i]);
					//propSpecs[i].Name_Or_ID = Marshal.StringToHGlobalUni(keys[i]);
					propSpecs[i].Name_Or_ID = Marshal.StringToCoTaskMemUni(keys[i]);
					propVariants[i] = new PropVariant();
				}
				//result = ps.ReadMultiple((uint)keys.Length, propSpecs, propVariants);
				//for (var i = 0; i < keys.Length; i++)
				//    os[i] = Marshal.PtrToStringUni(propVariants[i].pointerValue);
				for (var i = 0; i < keys.Length; i++)
				{
					result = ps.ReadMultiple(1, ref propSpecs[i], ref propVariants[i]);
					if (result == (uint)HResults.S_OK)
						os[i] = propVariants[i].ToObject();
					Marshal.FreeCoTaskMem(propSpecs[i].Name_Or_ID);
				}
				result = ps.Commit((int)STGC.DEFAULT);
				if (result != (uint)HResults.S_OK)
					return os;
			}
			finally
			{
				if (pss != null)
					Marshal.ReleaseComObject(pss);
				if (ps != null)
					Marshal.ReleaseComObject(ps);
				pss = null;
				ps = null;
			}
			return os;
		}
	}
}
